package com.yunquan.ohana.base.bus

import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject
import io.reactivex.rxjava3.subjects.Subject
import java.util.concurrent.ConcurrentHashMap

/**
 * 只会把在订阅发生的时间点之后来自原始Observable的数据发射给观察者
 */
class RxBus {
    companion object {
        private var mInstance: RxBus? = null

        @Synchronized
        fun getDefault(): RxBus {
            if (mInstance == null) {
                synchronized(RxBus::class.java) {
                    if (mInstance == null) {
                        mInstance = RxBus()
                    }
                }
            }
            return mInstance!!
        }
    }

    private val mBus: Subject<Any> = PublishSubject.create<Any>().toSerialized()
    private val mStickyEventMap = ConcurrentHashMap<Class<*>, Any>()

    fun post(event: Any) {
        mBus.onNext(event)
    }

    fun <T : Any> toObservable(eventType: Class<T>): Observable<T> {
        return mBus.ofType(eventType)
    }

    fun hasObservers(): Boolean {
        return mBus.hasObservers()
    }

    fun reset() {
        mInstance = null
    }

    fun postSticky(event: Any) {
        synchronized(mStickyEventMap) {
            mStickyEventMap[event::class.java] = event
        }
        post(event)
    }

    fun <T : Any> toObservableSticky(eventType: Class<T>): Observable<T> {
        synchronized(mStickyEventMap) {
            val observable = mBus.ofType(eventType)
            val event = mStickyEventMap[eventType]
            if (event != null) {
                return Observable.merge(observable, Observable.create { emitter ->
                    eventType.cast(event)?.let { emitter.onNext(it) }
                })
            } else {
                return observable
            }
        }
    }

    fun <T> getStickyEvent(eventType: Class<T>): T {
        synchronized(mStickyEventMap) {
            return eventType.cast(mStickyEventMap[eventType])!!
        }
    }

    fun <T> removeStickyEvent(eventType: Class<T>): T {
        synchronized(mStickyEventMap) {
            return eventType.cast(mStickyEventMap[eventType])!!
        }
    }

    fun removeAllStickyEvents() {
        synchronized(mStickyEventMap) {
            mStickyEventMap.clear()
        }
    }
}